#include "GLSLProgram.h"
GLSLProgram::GLSLProgram()
{
    isLinked = false;
    handle = glCreateProgram();
    if(handle == 0)
    {
        std::cerr<<"Cannot create program object\n";
        exit(0);
    }
}

bool GLSLProgram::AddShaderFromFile(std::string filename, GLenum type)
{
    std::ifstream in(filename.c_str());
    if(!in)
    {
        std::cerr<<"Unable to open file: "<<filename<<std::endl;
        exit(0);
    }

    std::string progContent((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());

    in.close();

    GLuint shader = glCreateShader(type);
    char *source = new char[progContent.size() + 1];
    memset(source, 0, progContent.size() + 1);
    memcpy(source, progContent.c_str(), progContent.size());
    glShaderSource(shader, 1, (GLchar**)(&source), NULL);
    delete []source;
    glCompileShader(shader);

    GLint flag;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &flag);
    if(!flag)
    {
        GLsizei length = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
        char *log = (char*)malloc(length * sizeof(char));
        glGetShaderInfoLog(shader, length * sizeof(char), &length, log);

        std::cerr<<"Compile log:\n";
        if(type == GL_VERTEX_SHADER)
            std::cerr<<"Vertex shader:\n"<<log<<std::endl;
        else
            std::cerr<<"Fragment shader:\n"<<log<<std::endl;
        return false;
    }

    glAttachShader(handle, shader);
    return true;
}

bool GLSLProgram::AddShaderFromString(const std::string &progContent, GLenum type)
{
    GLuint shader = glCreateShader(type);
    char *source = new char[progContent.size() + 1];
    memset(source, 0, progContent.size() + 1);
    memcpy(source, progContent.c_str(), progContent.size());
    glShaderSource(shader, 1, (GLchar**)(&source), NULL);
    delete []source;
    glCompileShader(shader);

    GLint flag;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &flag);
    if(!flag)
    {
        GLsizei length = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
        char *log = (char*)malloc(length * sizeof(char));
        glGetShaderInfoLog(shader, length * sizeof(char), &length, log);

        std::cerr<<"Compile log:\n"<<log<<std::endl;
        free(log);

        return false;
    }

    glAttachShader(handle, shader);
    return true;
}

bool GLSLProgram::Validation()
{
    glValidateProgram(handle);

    GLint flag;
    glGetProgramiv(handle, GL_VALIDATE_STATUS, &flag);
    if(!flag)
    {
        GLsizei length = 0;
        glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &length);
        char *log = (char*)malloc(length * sizeof(char));
        glGetProgramInfoLog(handle, length * sizeof(char), &length, log);

        std::cerr<<"Validation log:\n"<<log<<std::endl;
        free(log);

        return false;
    }

    return true;
}

bool GLSLProgram::Link()
{
    glLinkProgram(handle);

    GLint flag;
    glGetProgramiv(handle, GL_LINK_STATUS, &flag);
    if(!flag)
    {
        GLsizei length = 0;
        glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &length);
        char *log = (char*)malloc(length * sizeof(char));
        glGetProgramInfoLog(handle, length * sizeof(char), &length, log);

        std::cerr<<"Link log:\n"<<log<<std::endl;
        free(log);

        return false;
    }

    return true;
}

void GLSLProgram::PrintActiveUniforms()
{
    GLint nUniforms, maxLen;
    glGetProgramiv(handle, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLen);
    glGetProgramiv(handle, GL_ACTIVE_UNIFORMS, &nUniforms);

    char *name = (char*)malloc(maxLen);

    std::cout<<"Active uniforms:\n";
    std::cout<<"Location | Name\n";
    std::cout<<"----------------------------------------------------\n";
    for(int i = 0; i < nUniforms; ++i)
    {
        glGetActiveUniform(handle, i, maxLen, NULL, NULL, NULL, name);
        GLuint location = GetUniformLocation(name);

        std::cout<<location<<" | "<<name<<std::endl;
    }

    free(name);
}

void GLSLProgram::PrintActiveAttribs()
{
    GLint nAttribs, maxLen;
    glGetProgramiv(handle, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLen);
    glGetProgramiv(handle, GL_ACTIVE_ATTRIBUTES, &nAttribs);

    char *name = (char*)malloc(maxLen);

    std::cout<<"Active attributes:\n";
    std::cout<<"Location | Name";
    std::cout<<"----------------------------------------------------\n";
    for(int i = 0; i < nAttribs; ++i)
    {
        glGetActiveAttrib(handle, i, maxLen, NULL, NULL, NULL, name);
        GLuint location = glGetAttribLocation(handle, name);

        std::cout<<location<<" | "<<name<<std::endl;
    }
    free(name);
}
